package com.gobang.service.impl;

import com.gobang.common.exception.UserException;
import com.gobang.common.util.RedisUtil;
import com.gobang.common.util.ScoreUtils;
import com.gobang.domain.dto.UserRegisterDTO;
import com.gobang.domain.entity.User;
import com.gobang.domain.vo.UserInfoVO;
import com.gobang.mapper.UserMapper;
import com.gobang.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

/**
 * 用户管理业务服务实现类
 * 
 * @author System
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;
    private final RedisTemplate<String, Object> redisTemplate;
    private final BCryptPasswordEncoder passwordEncoder;
    private final RedisUtil redisUtil;

    private static final String USER_CACHE_KEY_PREFIX = "user:info:";
    private static final int USER_CACHE_EXPIRE_MINUTES = 30;
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UserInfoVO register(UserRegisterDTO registerDTO) {
        log.info("开始用户注册，用户名：{}", registerDTO.getUsername());

        // 1. 检查用户名是否已存在
        if (userMapper.existsByUsername(registerDTO.getUsername()) > 0) {
            log.warn("用户注册失败，用户名已存在：{}", registerDTO.getUsername());
            throw UserException.usernameAlreadyExists();
        }

        // 2. 密码加密
        String encodedPassword = passwordEncoder.encode(registerDTO.getPassword());
        log.debug("密码加密完成，用户名：{}", registerDTO.getUsername());

        // 3. 创建用户实体
        User user = new User();
        user.setUsername(registerDTO.getUsername());
        user.setPassword(encodedPassword);
        user.setCurrentScore(0);  // 初始积分为0
        user.setTotalGames(0);    // 初始总场次为0
        user.setWinGames(0);      // 初始获胜场数为0

        // 4. 保存到数据库
        int result = userMapper.insert(user);
        if (result <= 0) {
            log.error("用户注册失败，数据库插入失败，用户名：{}", registerDTO.getUsername());
            throw UserException.userCreateFailed();
        }

        log.info("用户注册成功，用户ID：{}，用户名：{}", user.getId(), user.getUsername());

        // 5. 初始化用户信息缓存（不包含在线状态）
        try {
            String cacheKey = USER_CACHE_KEY_PREFIX + user.getId();
            // redisTemplate.opsForHash().put(cacheKey, "username", user.getUsername());
            redisUtil.hSet(cacheKey, "username", user.getUsername());
            redisTemplate.opsForHash().put(cacheKey, "current_score", user.getCurrentScore());
            redisTemplate.opsForHash().put(cacheKey, "total_games", user.getTotalGames());
            redisTemplate.opsForHash().put(cacheKey, "win_games", user.getWinGames());
            redisTemplate.expire(cacheKey, USER_CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
            log.debug("用户信息缓存初始化成功，用户ID：{}", user.getId());
        } catch (Exception e) {
            log.error("用户信息缓存初始化失败，用户ID：{}，错误：{}", user.getId(), e.getMessage(), e);
            // 缓存失败不影响注册流程
        }

        // 6. 转换为VO对象返回
        return convertToUserInfoVO(user);
    }

    @Override
    public UserInfoVO getUserById(Long userId) {
        log.debug("查询用户信息，用户ID：{}", userId);

        // 1. 先尝试从Redis缓存获取基础信息
        String cacheKey = USER_CACHE_KEY_PREFIX + userId;
        String cachedUsername = null;
        try {
            cachedUsername = (String) redisTemplate.opsForHash().get(cacheKey, "username");
        } catch (Exception e) {
            log.warn("Redis缓存查询失败，用户ID：{}，错误：{}", userId, e.getMessage());
        }
        
        // 2. 从数据库查询完整信息（缓存只存基础信息，完整查询还是需要数据库）
        User user = userMapper.selectById(userId);
        if (user == null) {
            log.warn("用户不存在，用户ID：{}", userId);
            throw UserException.userNotFound();
        }

        // 3. 更新缓存（如果缓存中没有或信息不一致）
        if (cachedUsername == null || !cachedUsername.equals(user.getUsername())) {
            try {
                redisTemplate.opsForHash().put(cacheKey, "username", user.getUsername());
                redisTemplate.opsForHash().put(cacheKey, "current_score", user.getCurrentScore());
                redisTemplate.opsForHash().put(cacheKey, "total_games", user.getTotalGames());
                redisTemplate.opsForHash().put(cacheKey, "win_games", user.getWinGames());
                redisTemplate.expire(cacheKey, USER_CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
                log.debug("用户信息缓存更新成功，用户ID：{}", userId);
            } catch (Exception e) {
                log.warn("用户信息缓存更新失败，用户ID：{}，错误：{}", userId, e.getMessage());
            }
        }

        log.debug("用户信息查询成功，用户ID：{}，用户名：{}", userId, user.getUsername());

        // 4. 转换为VO对象返回
        return convertToUserInfoVO(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateUserGameStats(Long userId, Integer scoreChange, boolean isWin) {
        log.info("更新用户游戏统计，用户ID：{}，积分变化：{}，是否获胜：{}", userId, scoreChange, isWin);

        // 1. 验证参数
        if (userId == null || scoreChange == null) {
            log.error("用户游戏统计更新失败，参数无效，用户ID：{}，积分变化：{}", userId, scoreChange);
            throw UserException.userStatusInvalid();
        }

        // 2. 更新数据库
        int isWinFlag = isWin ? 1 : 0;
        // 更新了 总积分、总场次、胜场数
        User user = userMapper.selectById(userId);
        // Integer newScore = Math.max(0, user.getCurrentScore() + scoreChange); // 积分不能为负
        Integer newScore = ScoreUtils.validateNewScore(user.getCurrentScore(), scoreChange);
        int result = userMapper.updateUserGameStats(userId, newScore, isWinFlag);
        if (result <= 0) {
            log.error("用户游戏统计更新失败，数据库更新失败，用户ID：{}", userId);
            throw UserException.userUpdateFailed();
        }

        // 3. 更新Redis缓存
        String cacheKey = USER_CACHE_KEY_PREFIX + userId;
        try {
            // 存在更新；不存在，无操作
            if (redisTemplate.hasKey(cacheKey)) {
                // 获取当前积分并更新
                Object currentScoreObj = redisTemplate.opsForHash().get(cacheKey, "current_score");
                Object totalGamesObj = redisTemplate.opsForHash().get(cacheKey, "total_games");
                Object winGamesObj = redisTemplate.opsForHash().get(cacheKey, "win_games");
                
                if (currentScoreObj != null && totalGamesObj != null && winGamesObj != null) {
                    Integer currentScore = Integer.valueOf(currentScoreObj.toString());
                    Integer totalGames = Integer.valueOf(totalGamesObj.toString());
                    Integer winGames = Integer.valueOf(winGamesObj.toString());
                    
                    // Integer newScore = Math.max(0, currentScore + scoreChange); // 积分不能为负
                    Integer newTotalGames = totalGames + 1;
                    Integer newWinGames = isWin ? winGames + 1 : winGames;
                    
                    redisTemplate.opsForHash().put(cacheKey, "current_score", newScore);
                    redisTemplate.opsForHash().put(cacheKey, "total_games", newTotalGames);
                    redisTemplate.opsForHash().put(cacheKey, "win_games", newWinGames);
                    redisTemplate.expire(cacheKey, USER_CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
                    
                    log.debug("用户游戏统计缓存更新成功，用户ID：{}，新积分：{}，新总场次：{}，新获胜场数：{}", 
                             userId, newScore, newTotalGames, newWinGames);
                }
            }
        } catch (Exception e) {
            log.warn("用户游戏统计缓存更新失败，用户ID：{}，错误：{}", userId, e.getMessage());
            // 缓存更新失败不影响业务流程，数据库已更新成功
        }

        log.info("用户游戏统计更新成功，用户ID：{}", userId);
        return true;
    }

    @Override
    public User findByUsername(String username) {
        log.debug("根据用户名查询用户，用户名：{}", username);
        
        if (username == null || username.trim().isEmpty()) {
            log.warn("用户名为空，无法查询");
            return null;
        }
        
        User user = userMapper.findByUsername(username);
        if (user != null) {
            log.debug("用户查询成功，用户ID：{}，用户名：{}", user.getId(), user.getUsername());
        } else {
            log.debug("用户不存在，用户名：{}", username);
        }
        
        return user;
    }

    /**
     * 将User实体转换为UserInfoVO
     *
     * @param user 用户实体
     * @return 用户信息VO
     */
    private UserInfoVO convertToUserInfoVO(User user) {
        UserInfoVO vo = new UserInfoVO();
        vo.setId(user.getId());
        vo.setUsername(user.getUsername());
        vo.setCurrentScore(user.getCurrentScore());
        vo.setTotalGames(user.getTotalGames());
        vo.setWinGames(user.getWinGames());
        
        // 计算胜率
        if (user.getTotalGames() > 0) {
            vo.setWinRate((double) user.getWinGames() / user.getTotalGames());
        } else {
            vo.setWinRate(0.0);
        }
        
        if (user.getCreatedAt() != null) {
            vo.setCreatedAt(user.getCreatedAt().format(DATE_TIME_FORMATTER));
        }
        
        return vo;
    }
} 